package biz.datalk.industrialland.admin.modules.common.servie.impl;

import biz.datalk.industrialland.admin.component.shiro.CustomLoginUser;
import biz.datalk.industrialland.admin.modules.common.servie.IFileService;
import biz.datalk.industrialland.admin.modules.util.ShiroUtil;
import biz.datalk.industrialland.common.def.CommonConstant;
import biz.datalk.industrialland.common.def.enums.DBYNEnum;
import biz.datalk.industrialland.common.exception.ServiceException;
import biz.datalk.industrialland.common.log.def.LogTypeEnum;
import biz.datalk.industrialland.common.log.def.OptEnum;
import biz.datalk.industrialland.common.log.def.OptResultEnum;
import biz.datalk.industrialland.common.log.service.ISysLogService;
import biz.datalk.industrialland.common.log.util.LogJwtUtil;
import biz.datalk.industrialland.common.result.UnifyResultCode;
import biz.datalk.industrialland.common.util.DownloadUtil;
import biz.datalk.industrialland.common.util.JwtUtil;
import biz.datalk.industrialland.common.util.RedisUtil;
import biz.datalk.industrialland.common.util.ReqErrUtil;
import biz.datalk.industrialland.mbg.mapper.SysFileUploadMapper;
import biz.datalk.industrialland.mbg.pojo.po.SysFileUpload;
import biz.datalk.upload.model.FileObject;
import biz.datalk.upload.model.UploadResult;
import biz.datalk.upload.service.IUploadService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.Date;
import java.util.Map;
import java.util.function.Supplier;

/**
 * @author tarofang@163.com
 * @date 2024-01-20
 */
@Slf4j
@Service
public class FileServiceImpl implements IFileService {

    private final IUploadService uploadService;
    private final RedisUtil redisUtil;
    private final SysFileUploadMapper sysFileUploadMapper;
    private final ISysLogService sysLogService;


    public FileServiceImpl(
            IUploadService uploadService, //
            RedisUtil redisUtil, //
            SysFileUploadMapper sysFileUploadMapper, //
            ISysLogService sysLogService) {
        this.uploadService = uploadService;
        this.redisUtil = redisUtil;
        this.sysFileUploadMapper = sysFileUploadMapper;
        this.sysLogService = sysLogService;
    }

    /** 存储文件的服务器的IP地址 */
    @Value("${upload.local.storage-ip:}")
    private String uploadLocalStorageIp;
    /** 预览的 URL 前缀 */
    @Value("${upload.view-with-token-url-prefix}")
    private String viewWithTokenUrlPrefix;


    // @Override
    // public UnifyResult upload(String type, HttpServletRequest request) {
    //     request.setAttribute(CommonConstant.FILE_REQ_ATTR_IS_UPLOAD_API_KEY, CommonConstant.FILE_REQ_ATTR_IS_UPLOAD_API_VAL);
    //     if (!ServletFileUpload.isMultipartContent(request)) {
    //         log.warn("Invalid request content-type. [content-type={}]", request.getContentType());
    //         return UnifyResult.fail("文件上传请求错误");
    //     }
    //
    //     MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
    //     MultipartFile mf = multipartRequest.getFile("file");
    //     if (mf == null) {
    //         log.warn("MultipartFile is null. File Field is \"{}\"", "file");
    //         return UnifyResult.fail("未上传文件");
    //     }
    //
    //     String originalFilename = mf.getOriginalFilename();
    //
    //     // 校验文件的后缀
    //     String fileExtension = FileUtil.getFileExtension(originalFilename);
    //     if (StringUtils.isBlank(fileExtension)) {
    //         log.warn("fileExtension is blank. [originalFilename={}]", originalFilename);
    //         return UnifyResult.fail("不支持该类型的文件");
    //     }
    //     String validExtensionKey = String.format(CommonConstant.FILE_UPLOAD_VALID_EXTENSION_PATTERN, type);
    //     String validExtensionStr = redisUtil.getStr(validExtensionKey, StringUtils.EMPTY);
    //     validExtensionStr = StringUtils.isBlank(validExtensionStr) //
    //             ? sharedGarageXcxProperties.getTypeValidExtension().getOrDefault(type, StringUtils.EMPTY) //
    //             : validExtensionStr;
    //     String checkExtension = StringUtils.join(";", fileExtension, ";");
    //     if (!StringUtils.containsIgnoreCase(validExtensionStr, checkExtension)) {
    //         log.warn("fileExtension is not in validExtension. [checkExtension={}, validExtensionStr={}]", checkExtension, validExtensionStr);
    //         return UnifyResult.fail(String.format("不支持该类型文件[%s]", fileExtension));
    //     }
    //     // 校验文件的大小
    //     long fileSize = mf.getSize();
    //     String maxSizeKey = String.format(CommonConstant.FILE_UPLOAD_MAX_SIZE_PATTERN, type);
    //     String maxSizeStr = redisUtil.getStr(maxSizeKey, StringUtils.EMPTY);
    //     long maxSize = BasicParserUtils.parseLong(maxSizeStr, 5 * 1024 * 1024L);
    //     if (fileSize > maxSize) {
    //         log.warn("fileSize is too large. [fileSize={}, maxSize={}]", fileSize, maxSize);
    //         return UnifyResult.fail(String.format("上传文件过大[%s]，超过[%s]", FileUtil.formatFileSizes(fileSize, "#.##"), FileUtil.formatFileSizes(maxSize, "#.##")));
    //     }
    //
    //     // 保存文件数据
    //     UploadResult uploadResult;
    //     try {
    //         uploadResult = uploadService.upload(originalFilename, mf.getInputStream(), null);
    //     } catch (Exception ex) {
    //         log.error("{}", ex.getMessage(), ex);
    //         uploadResult = new UploadResult();
    //         uploadResult.setCode("-1");
    //         uploadResult.setMessage(ex.getMessage());
    //         uploadResult.setBucketName(StringUtils.EMPTY);
    //         uploadResult.setObjKey(StringUtils.EMPTY);
    //         uploadResult.setFileSize(0L);
    //         uploadResult.setFileContentType(StringUtils.EMPTY);
    //     }
    //     if (log.isInfoEnabled()) {
    //         log.info("file save result[saveResult={}, mfFileSize={}]", JsonUtil.getJsonFromObject(uploadResult), fileSize);
    //     }
    //
    //     // 保存上传的数据
    //     String fileCode = IdProvider.createUUIDId();
    //     saveUploadResult(type, originalFilename, fileCode, uploadResult);
    //
    //     if (!StringUtils.equals(uploadResult.getCode(), "0")) {
    //         log.warn("File upload error.[uploadResult={}]", JsonUtil.getJsonFromObject(uploadResult));
    //         return UnifyResult.fail("文件上传失败");
    //     }
    //
    //     String viewUrl = viewWithTokenUrlPrefix + "/" + fileCode;
    //     FileUploadVO vo = new FileUploadVO();
    //     vo.setFileCode(fileCode);
    //     vo.setObjKey(uploadResult.getObjKey());
    //     vo.setViewUrl(viewUrl);
    //     return UnifyResult.success().setData(vo);
    // }
    //
    // @Override
    // public void download(String mod, String fileCode, HttpServletRequest request, HttpServletResponse response) {
    //     // 跳过 AutoLogAspect 日志输出
    //     request.setAttribute(CommonConstant.FILE_REQ_ATTR_IS_DOWNLOAD_API_KEY, CommonConstant.FILE_REQ_ATTR_IS_DOWNLOAD_API_VAL);
    //
    //     SysFileUpload sysFileUpload = sysFileUploadMapper.selectByFileCode(fileCode);
    //     if (sysFileUpload == null) {
    //         response.setStatus(404);
    //         return;
    //     }
    //
    //     FileObject object = uploadService.getObject(sysFileUpload.getObjKey());
    //     String contentType = object.getContentType();
    //     long fileLength = object.getFileSize();
    //     InputStream is = object.getInputStream();
    //
    //     DownloadUtil.download("f".equals(mod), contentType, fileLength, StringUtils.trimToEmpty(sysFileUpload.getFileName()), is, request, response);
    // }
    //
    // @Override
    // public void view(String mod, String fileCode, HttpServletRequest request, HttpServletResponse response) {
    //     String optResult = "1";
    //     long sysUserId = -1L;
    //     String username = StringUtils.EMPTY;
    //     long start = System.currentTimeMillis();
    //     try {
    //         // 跳过 AutoLogAspect 日志输出
    //         request.setAttribute(CommonConstant.FILE_REQ_ATTR_IS_DOWNLOAD_API_KEY, CommonConstant.FILE_REQ_ATTR_IS_DOWNLOAD_API_VAL);
    //
    //         // 获取用户信息
    //         String accessToken = getAccessToken(request);
    //         if (StringUtils.isBlank(accessToken)) {
    //             response.setStatus(403); // 无权限访问
    //             ReqErrUtil.reqApi(response, UnifyResultCode.SERVICE_ERROR, "无权限访问");
    //             optResult = "0";
    //             return;
    //         }
    //         String tokenCacheKey = CommonConstant.PREFIX_USER_TOKEN.concat(accessToken);
    //         String jwtToken = redisUtil.getStr(tokenCacheKey, StringUtils.EMPTY);
    //         if (StringUtils.isBlank(jwtToken)) {
    //             response.setStatus(403); // 无权限访问
    //             ReqErrUtil.reqApi(response, UnifyResultCode.SERVICE_ERROR, "无访问权限");
    //             optResult = "0";
    //             return;
    //         }
    //         Map<String, String> payloads = LogJwtUtil.getPayloads(jwtToken, JwtUtil.PAYLOAD_SYS_USER_ID, JwtUtil.PAYLOAD_USERNAME);
    //         sysUserId = MapUtils.getLong(payloads, JwtUtil.PAYLOAD_SYS_USER_ID, -1L);
    //         username = MapUtils.getString(payloads, JwtUtil.PAYLOAD_USERNAME, StringUtils.EMPTY);
    //
    //         SysFileUpload sysFileUpload = sysFileUploadMapper.selectByFileCode(fileCode);
    //         if (sysFileUpload == null) {
    //             response.setStatus(404);
    //             ReqErrUtil.reqApi(response, UnifyResultCode.SERVICE_ERROR, "请求的资源不存在");
    //             optResult = "0";
    //             return;
    //         }
    //
    //         FileObject object = uploadService.getObject(sysFileUpload.getObjKey());
    //         String contentType = object.getContentType();
    //         long fileLength = object.getFileSize();
    //         InputStream is = object.getInputStream();
    //
    //         DownloadUtil.download("f".equals(mod), contentType, fileLength, StringUtils.trimToEmpty(sysFileUpload.getFileName()), is, request, response);
    //     } finally {
    //         String reqResult = StringUtils.EMPTY;
    //         String reqBody = StringUtils.EMPTY;
    //         OptResultEnum optResultEnum = "1".equals(optResult) ? OptResultEnum.SUCCESS : OptResultEnum.FAIL;
    //         long end = System.currentTimeMillis();
    //         sysLogService.saveLog(sysUserId, username, LogTypeEnum.OPERATE, OptEnum.XCX_FILE_20403, OptEnum.XCX_FILE_20403.getDescribe(), optResultEnum, reqResult, reqBody, end - start);
    //     }
    // }

    /**
     * 1. 从 header 中获取 access-token
     * 2. 从 cookie 中获取 access-token
     * 3. 从 url 中 获取 access_token
     *
     * @param request {@link HttpServletRequest}
     *
     * @return token
     */
    private String getAccessToken(HttpServletRequest request) {
        String accessToken = request.getHeader(CommonConstant.ACCESS_TOKEN);
        if (StringUtils.isNotBlank(accessToken)) {
            return accessToken;
        }

        Cookie[] cookies = request.getCookies();
        if (ArrayUtils.isNotEmpty(cookies)) {
            for (Cookie cookie : cookies) {
                if (StringUtils.equals(cookie.getName(), CommonConstant.ACCESS_TOKEN)) {
                    return cookie.getValue();
                }
            }
        }

        return request.getParameter(CommonConstant.URL_ACCESS_TOKEN);
    }


    private void saveUploadResult(String type, String originalFilename, String fileCode, UploadResult uploadResult) {
        try {
            // 获取必要数据
            CustomLoginUser customLoginUser = ShiroUtil.currentLoginUser();
            // ParkingUserInfo parkingUserInfo = customLoginUser.getParkingUserInfo();
            Long id = -1L;
            // String userNo = parkingUserInfo.getUserNo();
            int dbFileNameMaxLen = 250;
            String dbFileName = StringUtils.trimToEmpty(originalFilename);
            dbFileName = StringUtils.length(dbFileName) > dbFileNameMaxLen //
                    ? dbFileName.substring(0, dbFileNameMaxLen) //
                    : dbFileName;
            String storageType = StringUtils.trimToEmpty(uploadService.getStorageType());
            String storageIp = StringUtils.EMPTY;
            if (StringUtils.equals(storageType, "local")) {
                storageIp = this.uploadLocalStorageIp;
            }
            int uploadStatus = StringUtils.equals(uploadResult.getCode(), "0") ? 1 : 2;


            Date now = new Date();
            SysFileUpload sfu = new SysFileUpload();
            sfu.setFileCode(fileCode);
            sfu.setObjKey(uploadResult.getObjKey());
            sfu.setFileSize(uploadResult.getFileSize());
            sfu.setFileName(dbFileName);
            sfu.setStorageType(storageType);
            sfu.setBucketName(uploadResult.getBucketName());
            sfu.setStorageIp(storageIp);
            sfu.setFileContentType(uploadResult.getFileContentType());
            sfu.setUploadStatus(uploadStatus); // 上传状态 0-正在上传 1-上传成功 2-上传失败
            sfu.setUploadType(StringUtils.trimToEmpty(type));
            sfu.setIsValid(DBYNEnum.Y.name());
            sfu.setIsDelete(DBYNEnum.N.name());
            sfu.setCreateBy(id);
            sfu.setCreateName(StringUtils.EMPTY);
            sfu.setCreateTime(now);
            sfu.setUpdateBy(0L);
            sfu.setUpdateName(StringUtils.EMPTY);
            sfu.setUpdateTime(now);
            int resInt = sysFileUploadMapper.insertSelective(sfu);
            if (log.isDebugEnabled()) {
                log.debug("save SysFileUpload success. [resInt={}, fileCode={}]", resInt, fileCode);
            }

        } catch (Exception ex) {
            log.error("{}", ex.getMessage(), ex);
        }
    }

    @Override
    public SysFileUpload selectByFileCode(String fileCode) {
        if (StringUtils.isBlank(fileCode)) {
            log.warn("Blank fileCode.");
            return null;
        }

        return sysFileUploadMapper.selectByFileCode(fileCode.trim());
    }

    @Override
    public String getFileSignUrlByObjKey(String objKey) {
        if (StringUtils.isBlank(objKey)) {
            log.warn("Blank objKey.");
            return null;
        }
        return uploadService.getSignedUrl(objKey);
    }

    @Override
    public String getFileSignUrlByFileCode(String fileCode, Supplier<String> fileNoExistsErrMsgSupplier) {
        SysFileUpload sysFileUpload = this.selectByFileCode(fileCode);
        if (sysFileUpload == null) {
            log.warn("Invalid fileCode, sysFileUpload is null.[fileCode={}]", fileCode);
            String errMsg = fileNoExistsErrMsgSupplier != null ? fileNoExistsErrMsgSupplier.get() : "文件不存在或已被删除";
            throw new ServiceException(errMsg);
        }
        return uploadService.getSignedUrl(sysFileUpload.getObjKey());
    }

    @Override
    public void signedView(String mod, String fileSignedCode, HttpServletRequest request, HttpServletResponse response) {
        String optResult = "1";
        long sysUserId = -1L;
        String username = StringUtils.EMPTY;
        long start = System.currentTimeMillis();
        try {
            // 跳过 AutoLogAspect 日志输出
            request.setAttribute(CommonConstant.FILE_REQ_ATTR_IS_DOWNLOAD_API_KEY, CommonConstant.FILE_REQ_ATTR_IS_DOWNLOAD_API_VAL);

            if (StringUtils.isBlank(fileSignedCode)) {
                response.setStatus(HttpStatus.FORBIDDEN.value()); // 无权限访问
                ReqErrUtil.reqApi(response, UnifyResultCode.SERVICE_ERROR, "无权限访问");
                optResult = "0";
                return;
            }
            String cacheKey = "FILE_SIGNED_URL:" + fileSignedCode;
            String cacheObjKey = redisUtil.getStr(cacheKey, StringUtils.EMPTY);
            if (StringUtils.isBlank(cacheObjKey)) {
                response.setStatus(HttpStatus.NOT_FOUND.value()); // 无权限访问
                ReqErrUtil.reqApi(response, UnifyResultCode.SERVICE_ERROR, "访问链接已过期");
                optResult = "0";
            }

            SysFileUpload sysFileUpload = sysFileUploadMapper.selectByObjKey(StringUtils.trimToEmpty(cacheObjKey));
            if (sysFileUpload == null) {
                response.setStatus(HttpStatus.NOT_FOUND.value());
                ReqErrUtil.reqApi(response, UnifyResultCode.SERVICE_ERROR, "访问的文件不存在或已被删除");
                optResult = "0";
            }

            FileObject object = uploadService.getObject(cacheObjKey);
            String contentType = object.getContentType();
            long fileLength = object.getFileSize();
            InputStream is = object.getInputStream();
            String filename = object.getFilename();
            DownloadUtil.download("f".equals(mod), contentType, fileLength, filename, is, request, response);
        } catch (Exception ex) {
            log.error("{}", ex.getMessage(), ex);
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            ReqErrUtil.reqApi(response, UnifyResultCode.SERVICE_ERROR, "服务器发生错误，请稍后再试");
            optResult = "0";
        } finally {
            String reqResult = StringUtils.EMPTY;
            String reqBody = StringUtils.EMPTY;
            OptResultEnum optResultEnum = "1".equals(optResult) ? OptResultEnum.SUCCESS : OptResultEnum.FAIL;
            long end = System.currentTimeMillis();
            sysLogService.saveLog(sysUserId, username, LogTypeEnum.OPERATE, OptEnum.LAND_FILE_10701, OptEnum.LAND_FILE_10701.getDescribe(), optResultEnum, reqResult, reqBody, end - start);
        }
    }

    @Override
    public void view(String mod, String fileCode, HttpServletRequest request, HttpServletResponse response) {
        String optResult = "1";
        long sysUserId = -1L;
        String username = StringUtils.EMPTY;
        long start = System.currentTimeMillis();
        try {
            // 跳过 AutoLogAspect 日志输出
            request.setAttribute(CommonConstant.FILE_REQ_ATTR_IS_DOWNLOAD_API_KEY, CommonConstant.FILE_REQ_ATTR_IS_DOWNLOAD_API_VAL);

            // 获取用户信息
            String accessToken = getAccessToken(request);
            if (StringUtils.isBlank(accessToken)) {
                response.setStatus(403); // 无权限访问
                ReqErrUtil.reqApi(response, UnifyResultCode.SERVICE_ERROR, "无权限访问");
                optResult = "0";
                return;
            }
            String tokenCacheKey = CommonConstant.PREFIX_USER_TOKEN.concat(accessToken);
            String jwtToken = redisUtil.getStr(tokenCacheKey, StringUtils.EMPTY);
            if (StringUtils.isBlank(jwtToken)) {
                response.setStatus(403); // 无权限访问
                ReqErrUtil.reqApi(response, UnifyResultCode.SERVICE_ERROR, "无访问权限");
                optResult = "0";
                return;
            }
            Map<String, String> payloads = LogJwtUtil.getPayloads(jwtToken, JwtUtil.PAYLOAD_SYS_USER_ID, JwtUtil.PAYLOAD_USERNAME);
            sysUserId = MapUtils.getLong(payloads, JwtUtil.PAYLOAD_SYS_USER_ID, -1L);
            username = MapUtils.getString(payloads, JwtUtil.PAYLOAD_USERNAME, StringUtils.EMPTY);

            SysFileUpload sysFileUpload = sysFileUploadMapper.selectByFileCode(fileCode);
            if (sysFileUpload == null) {
                response.setStatus(404);
                ReqErrUtil.reqApi(response, UnifyResultCode.SERVICE_ERROR, "请求的资源不存在");
                optResult = "0";
                return;
            }

            FileObject object = uploadService.getObject(sysFileUpload.getObjKey());
            String contentType = object.getContentType();
            long fileLength = object.getFileSize();
            InputStream is = object.getInputStream();

            String fileName = sysFileUpload.getFileName();

            DownloadUtil.download("f".equals(mod), contentType, fileLength, StringUtils.trimToEmpty(fileName), null, is, response, request);
        } finally {
            String reqResult = StringUtils.EMPTY;
            String reqBody = StringUtils.EMPTY;
            OptResultEnum optResultEnum = "1".equals(optResult) ? OptResultEnum.SUCCESS : OptResultEnum.FAIL;
            long end = System.currentTimeMillis();
            sysLogService.saveLog(sysUserId, username, LogTypeEnum.OPERATE, OptEnum.LAND_FILE_10702, OptEnum.LAND_FILE_10702.getDescribe(), optResultEnum, reqResult, reqBody, end - start);
        }
    }
}
